/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 *
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_ui.htm
 *
 * Zhiqim UI is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

+(function(Z)
{//BEGIN
// @version v1.1.0 @author zouzhigang 2015-11-12 新建与整理

// 调用举例:
// <input type="text" name="date" focus="Z.date(this)" onchange="alert(this.value);">
// <input type="text" name="datetime" focus="Z.datetime(this)" onchange="alert(this.value);">
// <input type="text" name="datetimeNoSecond" focus="Z.datetimeNoSecond(this)" onchange="alert(this.value);">

Z.Calendar = Z.Class.newInstance();
Z.Calendar.v = "8.0.4";
Z.Calendar.prototype =
{
    defaults:
    {
        hasTime: false,                  //默认不显示时间
        hasSecond: true,                 //默认显示时间时有秒

        selectYear: null,                //选中的年,要求数字
        selectMonth: null,               //选中的月,要求数字
        selectDay: null,                 //选中的日期,要求数字
        selectHour: null,                //选中的小时,要求2位字符串
        selectMinute: null,              //选中的分钟,要求2位字符串
        selectSecond: null,              //选中的秒,要求2位字符串

        elem: null,                      //当前对象
        styles: null,                    //自定义样式
        elemDate: null,                  //选中的时间
        currDate: new Date(),            //保存当前时间
        dateMap: new Z.HashMap()         //42个日期和ID对应关系
    },

    init: function()
    {
        this.$elem = Z.$elem(this.elem, "Z.Calendar");
        this.random = Z.random(10);//日历ID,为保证唯一在init随机生成

        this.html ='<div id="Z_Calendar_'+this.random+'" style="position:absolute;display:none;z-index:1000;">';
        this.html+='<table class="z-table z-calendar">';

        //选择年月
        this.html+='<tr>';
        this.html+='    <td>';
        this.html+='    <table class="z-table z-top">';
        this.html+='    <tr>';
        this.html+='        <td width="30" align="center" class="z-pointer" id="Z_Calendar_prev_'+this.random+'"><div class="z-arrow z-left"></div></td>';
        this.html+='        <td width="*" align="center" class="z-year-month" id="Z_Calendar_yearMonth_'+this.random+'"><span class="z-default"></span></td>';
        this.html+='        <td width="30" align="center" class="z-pointer" id="Z_Calendar_next_'+this.random+'"><div class="z-arrow z-right"></td>';
        this.html+='    </tr>';
        this.html+='    </table>';
        this.html+='    </td>';
        this.html+='</tr>';

        //当前星期
        this.html+='<tr>';
        this.html+='    <td>';
        this.html+='    <table class="z-table z-week z-px14">';
        this.html+='    <tr>';
        this.html+='        <td>日</td><td>一</td><td>二</td><td>三</td><td>四</td><td>五</td><td>六</td>';
        this.html+='    </tr>';
        this.html+='    </table>';
        this.html+='    </td>';
        this.html+='</tr>';

        //当前星期日期,6行7列
        this.html+='<tr>';
        this.html+='    <td height="180" valign="top">';
        this.html+='    <table class="z-table z-day">';

        var i, j, n = 0;
        for (i=0;i<6;i++)
        {
            this.html+= '<tr>';
            for (j=0;j<7;j++)
            {
                this.html+='  <td id="Z_Calendar_day_'+this.random+'_'+(++n)+'"></td>';
            }
            this.html+='</tr>';
        }

        this.html+='    </table>';
        this.html+='    </td>';
        this.html+='</tr>';

        //选择时分秒
        if (this.isTime())
        {
            this.html+='<tr>';
            this.html+=        '<td valign="top">';
            this.html+=        '<table class="z-table z-timebox">';
              this.html+=        '<tr>';
              this.html+=            '<td>';
              this.html+=            '<dl class="z-sliderline">';
              this.html+=                '<dt>小时：</dt>';
              this.html+=                '<span class="z-scrollbar"></span>';
              this.html+=                '<span class="z-sliderbox""><span class="z-slider" id="Z_Calendar_hour_'+this.random+'"></span></span>';
              this.html+=            '</dl>';
              this.html+=            '</td>';
              this.html+=        '</tr>';
              this.html+=        '<tr>';
              this.html+=            '<td>';
              this.html+=            '<dl class="z-sliderline">';
              this.html+=                '<dt>分钟：</dt>';
              this.html+=                '<span class="z-scrollbar"></span>';
              this.html+=                '<span class="z-sliderbox"><span class="z-slider" id="Z_Calendar_minute_'+this.random+'"></span></span>';
              this.html+=            '</dl>';
              this.html+=            '</td>';
              this.html+=        '</tr>';

              if (this.isSecond())
              {
                  this.html+=        '<tr>';
                  this.html+=            '<td>';
                  this.html+=            '<dl class="z-sliderline">';
                  this.html+=                '<dt>秒数：</dt>';
                  this.html+=                '<span class="z-scrollbar"></span>';
                  this.html+=                '<span class="z-sliderbox"><span class="z-slider" id="Z_Calendar_second_'+this.random+'"></span></span>';
                  this.html+=            '</dl>';
                  this.html+=            '</td>';
                  this.html+=        '</tr>';
              }

              this.html+=        '<tr>';
              this.html+=            '<td>';
              this.html+=            '<div class="z-time">时间：<span class="z-mg-l8" id="Z_Calendar_time_'+this.random+'">15:12:52</span></div>';
              this.html+=            '<div class="z-close"><div class="z-button z-blue z-small" id="Z_Calendar_close_'+this.random+'">完成</div></div>';
              this.html+=            '</td>';
              this.html+=        '</tr>';
        }

        //结束
        this.html+='</table>';
        this.html+='</div>';
    },

    execute: function()
    {
        //解析对象中日期时间，如果不是日期时间格式置为当天
        if ((this.hasTime && !Z.V.isDateTime(this.elem.value)) || (!this.hasTime && !Z.V.isDate(this.elem.value)))
            this.elemDate = this.currDate;
        else
            this.elemDate = Z.DT.toDate(this.elem.value);

        this.selectYear = this.elemDate.getFullYear();
        this.selectMonth = this.elemDate.getMonth()+1;
        this.selectDay = this.elemDate.getDate();
        this.selectHour = Z.S.prefixZero(this.elemDate.getHours(), 2);
        this.selectMinute = Z.S.prefixZero(this.elemDate.getMinutes(), 2);
        this.selectSecond = this.isSecond()?Z.S.prefixZero(this.elemDate.getSeconds(), 2):0;

        //组装日历DIV，和设置好位置和事件
        if (!this.hasTime)
            this.height = 253;
        else if (!this.hasSecond)
            this.height = 344;
        else
            this.height = 374;

        //判断位置
        var $appendParent = "body";
        var setCss = {display: "block"};
        var top, left;
        //判断是不是在 dialog 内部
        var $parent = this.$elem.parent();
        var isInDialog = false;
        while ($parent[0].tagName.toLowerCase() !== "body")
        {
            $parent = $parent.parent();
            if ($parent.hasClass("z-dialog")) {
                isInDialog = true;
                break;
            }
        }
        //dialog 弹窗内的日历插件
        if (isInDialog)
        {
            $appendParent = $parent;
            setCss.zIndex = "20002";
            top = this.$elem.offsetTopBody() - $parent.offsetTopBody() + this.$elem.offsetHeight() + Z.D.scrollTop();
            left = this.$elem.offsetLeftBody() - $parent.offsetLeftBody() + Z.D.scrollLeft();

            if (this.$elem.offsetTopBody() > this.height &&
                Z.D.clientHeight() - this.$elem.offsetTopBody() - this.$elem.offsetHeight() - this.height < 0)
            {//如果顶部够高，底部不够高时，则向上弹出
                top = top - this.height - this.$elem.offsetHeight();
            }

            if (left + 283 + $parent.offsetLeftBody() > Z.D.clientWidth() &&
                left + $parent.offsetLeftBody() > 283 - this.$elem.offsetWidth())
            {//如果右侧不够宽，则靠左弹出
                left = left - 283 + this.$elem.offsetWidth();
            }
        }
        //普通的日历插件
        else
        {
            top = this.$elem.offsetTopBody() + this.$elem.offsetHeight();
            left = this.$elem.offsetLeftBody();

            if (top > this.height && Z.D.clientHeight() + Z.D.scrollTop() - top - this.height < 0)
            {//如果顶部够高，底部不够高时，则向上弹出
                top = top - this.height - this.$elem.offsetHeight();
            }

            if (left + 283 > Z.D.clientWidth() && left > 283 - this.$elem.offsetWidth())
            {//如果右侧不够宽，则靠左弹出
                left = left - 283 + this.$elem.offsetWidth();
            }
        }
        setCss.top = top;
        setCss.left = left;
        var $calendar = Z(this.html);
        $calendar.appendTo($appendParent).css(setCss);

        // 插入自定义样式
        var styles = this.styles || {};
        for (var i in styles)
        {
            $calendar.css(i, styles[i]);
        }

        //当前点击时阻止冒泡，其他点击时关闭
        $calendar.on("mousedown click", Z.E.forbidden);
        this.$elem.on("blur", this.close, this);

        //设置向左和向右移动一个月
        Z("#Z_Calendar_prev_"+this.random).click(this.doPrevMonth, this);
        Z("#Z_Calendar_next_"+this.random).click(this.doNextMonth, this);

        //设置清空,关闭
        Z("#Z_Calendar_clear_"+this.random).click(function(){this.elem.value="";}, this);
        Z("#Z_Calendar_close_"+this.random).click(function(e){this.close(e);}, this);

        //设置选择年份月份时打开年份列表事件
        Z("#Z_Calendar_yearMonth_"+this.random).click(this.doOpenYearList, this);

        //最后显示选择年份,月份和日期信息
        this.showYearMonth();
        this.showDay();

        if (this.isTime())
        {
            //初始化滑块位置和滑动动作
            var hour = this.selectHour * 200 / 23;
            var minute = this.selectMinute * 200 / 59;
            Z("#Z_Calendar_hour_"+this.random).css("left", hour+"px");
            Z("#Z_Calendar_minute_"+this.random).css("left", minute+"px");

            Z("#Z_Calendar_hour_"+this.random).drag({left:0,top:0,width:200,height:0,cursor:"pointer"}, this.onSlideHour, this);
            Z("#Z_Calendar_minute_"+this.random).drag({left:0,top:0,width:200,height:0,cursor:"pointer"}, this.onSlideMinute, this);

            if (this.isSecond())
            {
                var second = this.selectSecond * 200 / 59;
                Z("#Z_Calendar_second_"+this.random).css("left", second+"px");
                Z("#Z_Calendar_second_"+this.random).drag({left:0,top:0,width:200,height:0,cursor:"pointer"}, this.onSlideSecond, this);
            }

            //显示当前时间
            this.showTime();
        }
    },

    doOpenYearList: function(e)
    {//打开选择年份列表
        var $list = Z("#Z_Calendar_yearMonth_"+this.random).find("ul");
        if ($list.length > 0)
            $list.remove();
        else
        {
            var min = this.selectYear - 50;
            var max = this.selectYear + 50;
            var $ul = Z("<ul></ul>").addClass("z-year-list");
            for (var i=min;i<=max;i++)
            {
                var $option = Z("<span value='" + i + "'>" + i + "年" + "</span>");
                $option.click(this.onChangeYear, this);
                if (i == this.selectYear)
                    $option.addClass("z-selected");
                $ul.append($option);
            }
            $ul.append("<span class='z-close'>关闭</span>");
            Z("#Z_Calendar_yearMonth_"+this.random).append($ul);
            //把滚动条移到中间位置
            $ul[0].scrollTop = $ul[0].scrollHeight/2 - 117;
        }
    },

    onChangeYear: function(e)
    {//修改年份
        var value = Z(Z.E.target(e)).val();
        this.selectYear = parseInt(value);

        Z("#Z_Calendar_yearMonth_"+this.random).find("ul").hide().remove();

        this.showYearMonth();
        this.showDay();

        Z.E.stop(e);
    },

    onSlideHour: function(e)
    {//滑动小时
        var left = Z("#Z_Calendar_hour_"+this.random).css("left");
        left = parseFloat(Z.S.trimRight(left, "px"));
        this.selectHour = Math.floor(left * 23 / 200);
        this.showTime();
        this.setSelectValue();
    },

    onSlideMinute: function(e)
    {//滑动分钟
        var left = Z("#Z_Calendar_minute_"+this.random).css("left");
        left = parseFloat(Z.S.trimRight(left, "px"));
        this.selectMinute = Math.floor(left * 59 / 200);
        this.showTime();
        this.setSelectValue();
    },

    onSlideSecond: function(e)
    {//滑动秒
        var left = Z("#Z_Calendar_second_"+this.random).css("left");
        left = parseFloat(Z.S.trimRight(left, "px"));
        this.selectSecond = Math.floor(left * 59 / 200);
        this.showTime();
        this.setSelectValue();
    },

    doPrevMonth: function()
    {//上一月
        this.selectYear = this.selectMonth==1?this.selectYear-1:this.selectYear;
        this.selectMonth = this.selectMonth==1?12:this.selectMonth-1;

        //重置年月日
        this.showYearMonth();
        this.showDay();
    },

    doNextMonth: function()
    {//下一月
        this.selectYear = this.selectMonth==12?this.selectYear+1:this.selectYear;
        this.selectMonth = this.selectMonth==12?1:this.selectMonth+1;

        //重置年月日
        this.showYearMonth();
        this.showDay();
    },

    close: function(e)
    {//关闭
        this.$elem.off("blur", this.close, this);
        this.$elem.blur();
        Z("#Z_Calendar_"+this.random).remove();
    },

    showYearMonth: function()
    {//显示年月
        Z("#Z_Calendar_yearMonth_"+this.random).find(".z-default").html(this.selectYear+"年"+this.selectMonth+"月");
    },

    showDay: function()
    {//显示日期，6行7列的日期显示
        var i,id,day;

        var lastYear = this.selectMonth==1?this.selectYear-1:this.selectYear;
        var lastMonth = this.selectMonth==1?12:this.selectMonth-1;
        var nextYear = this.selectMonth==12?this.selectYear+1:this.selectYear;
        var nextMonth = this.selectMonth==12?1:this.selectMonth+1;

        var firstWeek = new Date(this.selectYear, this.selectMonth-1, 1).getDay(); //某月第一天的星期几
        if (firstWeek == 0)
            firstWeek = 7;//星期天是1号则向后推7天
        var curMonthMaxDay = Z.DT.getMonthDays(this.selectYear, this.selectMonth);
        var lastMonthMaxDay = Z.DT.getMonthDays(lastYear, lastMonth);

        //先统一背景等数据
        for (i=1;i<=42;i++)
        {
            id = "Z_Calendar_day_"+this.random+"_"+i;
            Z("#"+id).removeClass("z-nomonth").removeClass("z-tomonth").removeClass("z-today").removeClass("z-selected");
        }

        //1到firstWeek表示上月
        for (i=1;i<=firstWeek;i++)
        {
            id = "Z_Calendar_day_"+this.random+"_"+i;
            day = lastMonthMaxDay-firstWeek+i;

            this.dateMap.put(id, this.formatDate(lastYear, lastMonth, day));
            Z("#"+id).text(day);
        }

        //firstWeek+1到firstWeek+1+curMonthMaxDay表示当月
        for (i=firstWeek+1;i<firstWeek+1+curMonthMaxDay;i++)
        {
            id = "Z_Calendar_day_"+this.random+"_"+i;
            day = i-firstWeek;//日期从1到curMonthMaxDay

            this.dateMap.put(id, this.formatDate(this.selectYear, this.selectMonth, day));

            var $day = Z("#"+id).text(day);

            if (this.currDate.getDate() == day && this.currDate.getMonth()+1 == this.selectMonth && this.currDate.getFullYear() == this.selectYear)
            {//当天
                $day.text("今天").addClass("z-today");
            }

            if (this.elemDate.getDate() == day && this.elemDate.getMonth()+1 == this.selectMonth && this.elemDate.getFullYear() == this.selectYear)
            {//选中的
                $day.addClass("z-selected");
            }
            else
            {
                $day.addClass("z-tomonth");
            }
        }

        //firstWeek+1+curMonthMaxDay+1到42表示下月
        for (i=firstWeek+1+curMonthMaxDay;i<=41;i++)
        {
            id = "Z_Calendar_day_"+this.random+"_"+i;
            day = i-curMonthMaxDay-firstWeek;//日期从1到显示的最后一天

            this.dateMap.put(id, this.formatDate(nextYear, nextMonth, day));
            Z("#"+id).addClass("z-nomonth").html(day);
        }

        //最后对所有日期增加点击事件
        for (i=1;i<=41;i++)
        {
            id = "Z_Calendar_day_"+this.random+"_"+i;
            Z("#"+id).click(function(e)
            {
                var id = Z.E.target(e).id;
                this.setIdValue(id);
                this.close(e);

            }, this);
        }

        //最后一个表示清空
        id = "Z_Calendar_day_"+this.random+"_"+42;
        Z("#"+id).css("color", "#339a99").html("清空").click(function(e){this.elem.value="";this.close(e);}, this);
    },

    showTime: function()
    {//显示时间
        Z("#Z_Calendar_time_"+this.random).text(this.formatTime());
    },

    setSelectValue: function()
    {
        if (this.hasTime === true)
            this.setValue(this.formatDate() + " " + this.formatTime());
        else
            this.setValue(this.formatDate());
    },

    setIdValue: function(id)
    {
        var date = (!id)?Z.DT.toDateString(this.currDate):this.dateMap.get(id);
        if (this.hasTime === true)
            this.setValue(date + " " + this.formatTime());
        else
            this.setValue(date);
    },

    setValue: function(newValue)
    {
        var oldValue = this.elem.value;
        this.elem.value = newValue;
        if (this.elem.onchange && oldValue != this.elem.value)
            this.elem.onchange();
    },

    formatDate: function(year, month, day)
    {
        if (!year && !month && !day)
            return this.selectYear + "-" + Z.S.prefixZero(this.selectMonth, 2) + "-" + Z.S.prefixZero(this.selectDay, 2);
        else
            return year + "-" + Z.S.prefixZero(month, 2) + "-" + Z.S.prefixZero(day, 2);
    },

    formatTime: function()
    {
        return Z.S.prefixZero(this.selectHour, 2) + ":" + Z.S.prefixZero(this.selectMinute, 2) + ":" + Z.S.prefixZero(this.selectSecond, 2);
    },

    isTime: function()
    {
        return this.hasTime === true;
    },

    isSecond: function()
    {
        return this.hasSecond === true;
    }
};

Z.date = function(elem, styles)
{
    return new Z.Calendar({immediate:true, elem:elem, styles:styles});
}

Z.datetime = function(elem, styles)
{
    return new Z.Calendar({immediate:true, elem:elem, hasTime:true, styles:styles});
}

Z.datetimeNoSecond = function(elem, styles)
{
    return new Z.Calendar({immediate:true, elem:elem, hasTime:true, hasSecond:false, styles:styles});
}

//END
})(zhiqim);
